#include "iTips.h"

#include <filesystem>
#include <mutex>
#include <unordered_map>

#include "elog.h"
#include "elog_file.h"
#include "itips.pb.h"
#include "ws_constant.h"
#include "ws_response.h"
#include "image_processor/live_stacking/star_align.h"
#include "dwarf_utils.h"

using namespace std;
namespace fs = std::filesystem;

std::mutex error_code_mutex;

static unordered_map<int, int> itips_cmd_code {
    { CMD_ASTRO_START_CALIBRATION, 1 },
    { CMD_ASTRO_START_GOTO_DSO, 2 },
    { CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, 3 },
    { CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, 4 },
    { CMD_ASTRO_START_CAPTURE_RAW_DARK, 5 },
    { CMD_ASTRO_GO_LIVE, 6 },
    { CMD_FOCUS_START_ASTRO_AUTO_FOCUS, 7 },
};

static unordered_map<int, int> itips_error_code {
    { CODE_ASTRO_PLATE_SOLVING_FAILED, 5000 },
    { CODE_ASTRO_DARK_GAIN_OUT_OF_RANGE, 5001 },
    { CODE_ASTRO_DARK_NOT_FOUND, 5002 },
    { CODE_ASTRO_DARK_RUNNING, 5003 },
    { CODE_ASTRO_CALIBRATION_RUNNING, 5004 },
    { CODE_ASTRO_GOTO_RUNNING, 5005 },
    { CODE_ASTRO_RESET_PITCH_MOTOR_FAILED, 5006 },
    { CODE_ASTRO_NEED_CALIBRATION, 5007 },
    { CODE_ASTRO_GOTO_READ_MOTOR_POSITION_AND_PLATE_SOLVING_FAILED, 5008 },
    { CODE_ASTRO_NEED_GOTO, 5009 },
    { CODE_ASTRO_NEED_ADJUST_SHOOT_PARAM, 5010 },
    { CODE_ASTRO_CALIBRATION_PLATE_SOLVING_FAILED_TOO_MUCH, 511 },
    { CODE_STEP_MOTOR_LIMIT_POSITION_WARNING, 5012 },
    { CODE_STEP_MOTOR_LIMIT_POSITION_HITTED, 5013 },
    { WS_SDCARD_NOT_EXIST, 5014 },
    { WS_SDCARD_WRITE_ERROR, 5015 },
    { CODE_FOCUS_ASTRO_STAR_CLOSE_TO_BORDER, 5016 },
    { CODE_FOCUS_ASTRO_DATA_NOT_ENOUGH, 5017 },
    { CODE_FOCUS_ASTRO_DATA_ERROR, 5018 },
    { CODE_FOCUS_ASTRO_AUTO_FOCUS_FAST_ERROR, 5019 },
    { CODE_CAMERA_TELE_GET_IMAGE_FAILED, 5020 },
    { CODE_CAMERA_TELE_RUNNING_PHOTO, 5021 },
    { CODE_CAMERA_TELE_RUNNING_RECORD, 5022 },
    { CODE_CAMERA_TELE_RUNNING_PANORAMA, 5023 },
    { CODE_CAMERA_TELE_RUNNING_TIMELAPSE, 5024 },
    { CODE_CAMERA_TELE_RUNNING_CAPTURE_DARK, 5025 },
    { CODE_CAMERA_TELE_RUNNING_CAPTURE_LIVE_STACKING, 5026 },
    { CODE_CAMERA_TELE_OPEN_FAILED, 5027 },
};

static unordered_map<int, int (ITips::*)(WsPacket& ws_packet)> iTips_func_table {
    { CMD_ITIPS_GET, &ITips::getITips },
};

int ITips::handleMessage(WsPacket& ws_packet) {
    log_i("handle astro message, cmd = %d", ws_packet.cmd());

    //在表中查找函数指针并调用
    auto it = iTips_func_table.find(ws_packet.cmd());
    if (it != iTips_func_table.end()) {
        (this->*(it->second))(ws_packet);
    }
    else {
        log_i("Function not found");
    }

    return 0;
}

int ITips::getITips(WsPacket& ws_packet) {
    std::lock_guard<std::mutex> lock(error_code_mutex);

    ReqITipsGet req_itips_get;
    string serialized_data = ws_packet.data();

    if (!req_itips_get.ParseFromString(serialized_data)) {
        wsResITipsGet(CMD_ITIPS_GET, MODULE_ITIPS, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR, 0, "");
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    if (req_itips_get.mode() < ASTRO_MODE || req_itips_get.mode() > TIMELAPSE_MODE) {
        wsResITipsGet(CMD_ITIPS_GET, MODULE_ITIPS, CMD_TYPE_RESPONSE, -WS_INVAID_PARAM, 0, "");
        return -WS_INVAID_PARAM;
    }

    if (req_itips_get.mode() == ASTRO_MODE)
        getAstroITips(astro_mode_cmd_id_, astro_mode_error_code_);

    return 0;
}

int ITips::getAstroITips() {
    CommonStepITips step_dark;
    CommonStepITips step_focus;
    CommonStepITips step_calibration;
    CommonStepITips step_goto;
    CommonStepITips step_adjust_param;
    CommonStepITips step_live_stacking;

    // 检查SD卡
    bool has_sd_card = false;
    if (fs::exists("/sdcard/DWARF_II")) 
        has_sd_card = true;

    // 检查SD卡容量
    bool sd_card_available = false;
    SdCardInfo sdcard_info = system_.getSdCardAvailableAndTotal();
    if (sdcard_info.available_size > 0) 
        sd_card_available = true;

    // 检查方位轴是否复位
    bool reset_motor_yaw = true;
    int absolute_position_pulse = 0;
    if (motor_yaw_.getAbsolutePositionInPulse(STEP_MOTOR_MINISTEP_1, absolute_position_pulse) == -CODE_STEP_MOTOR_NEED_RESET)
        reset_motor_yaw = false;

    // 检查俯仰轴是否复位
    bool reset_motor_pitch = true;
    if (motor_pitch_.getAbsolutePositionInPulse(STEP_MOTOR_MINISTEP_1, absolute_position_pulse) == -CODE_STEP_MOTOR_NEED_RESET)
        reset_motor_pitch = false;

    // 场景检测
    int has_sun_moon = focus_.checkWideSunMoon();
    if ((has_sun_moon & 0x0000001) == 0x0000001) {// 拍日月流程

    } else if((has_sun_moon & 0x0000010) == 0x0000010) {// 白天/室内场景

    } else {// 拍星流程
        if (astro_.getCaptureDarkState() != STATE_IDLE) {
            step_dark.set_step_id(ASTRO_DARK);
            step_dark.set_step_status(UNDONE);
        } else {
            std::vector<DarkFrameParameter> dark_lost;
            astro_.checkLostDark(dark_lost);

            if (dark_lost.size() > 0) {
                std::string itips_code = ITipsCode(ASTRO_MODE, 0, CODE_ITIPS_GUIDE_CAPTURE_DARK).getITipsCode();

                CommonITips itips;
                itips.set_itips_status(WARNING);
                itips.set_itips_code(itips_code);

                step_dark.set_step_id(ASTRO_DARK);
                step_dark.set_step_status(WARNING);
                *step_dark.add_step_itips() = itips;
            } else {
                step_dark.set_step_id(ASTRO_DARK);
                step_dark.set_step_status(DONE);
            }
        }
    }

    return 0;
}

int ITips::getAstroITips(int astro_mode_cmd_id, int astro_mode_error_code) {
    std::string itips_code;
    float infinity_focus_point_from_file = 0;
    
    if (astro_mode_cmd_id != 0) {// 有操作
        int i_cmd_code = 0;

        auto it = itips_cmd_code.find(astro_mode_cmd_id);
        if (it != itips_cmd_code.end()) {
            i_cmd_code = it->second;
        } else {
            log_i("astro_mode_cmd_id not found:%d", astro_mode_cmd_id);
            return 0;
        }

        if (astro_mode_error_code < 0) {// 执行中
            itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, STATE_RUNNING).getITipsCode();
        } else if (astro_mode_error_code > 0) {// 错误
            it = itips_error_code.find(astro_mode_error_code);
            if (it != itips_error_code.end()) {
                int i_error_code = it->second;
                itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, i_error_code).getITipsCode();
                if (astro_.getAstroStateWithoutTrackerState() == STATE_IDLE) {
                    if (astro_mode_error_code == CODE_ASTRO_PLATE_SOLVING_FAILED || astro_mode_error_code == CODE_ASTRO_GOTO_READ_MOTOR_POSITION_AND_PLATE_SOLVING_FAILED ||
                        astro_mode_error_code == CODE_ASTRO_CALIBRATION_PLATE_SOLVING_FAILED_TOO_MUCH) {
                        int has_sun_moon = focus_.checkWideSunMoon();
                        if ((has_sun_moon & 0x0000001) == 0x0000001) {
                            std::string target_name;
                            astro_.gotoer_.getTargetName(target_name);

                            if (target_name != "Sun" && target_name != "Moon") {
                                getAstroITips(CMD_FOCUS_START_ASTRO_AUTO_FOCUS, WS_OK);  
                            }
                        } else if((has_sun_moon & 0x0000010) == 0x0000010) {

                        } else {

                        }

                        MEDIA_BUFFER dst_mb = cam_tele_.getRgbMB_4k(1920, 1080);
                        Mat img_src = Mat(1080, 1920, CV_8UC3, (unsigned char*)RK_MPI_MB_GetPtr(dst_mb));
                        if (StarDetection(img_src) == 0) {
                            getAstroITips(getStarLastProcedureCmd(astro_mode_cmd_id), WS_OK);  
                            return 0;
                        }
                        rkfree(dst_mb);
                    } else if (astro_mode_error_code == CODE_ASTRO_DARK_GAIN_OUT_OF_RANGE || astro_mode_error_code == CODE_ASTRO_NEED_ADJUST_SHOOT_PARAM) {
                        ExposureSetting exp_setting;
                        cam_tele_.getExp(exp_setting);

                        IntValueSetting gain_setting;
                        cam_tele_.getGain(gain_setting);

                        if (exp_setting.value >= 5.0 && gain_setting.value >= 30 && gain_setting.value <= 120) {
                            getAstroITips(getStarLastProcedureCmd(astro_mode_cmd_id), WS_OK);  
                            return 0;
                        }
                    } else if (astro_mode_error_code == WS_SDCARD_NOT_EXIST) {
                        if (fs::exists("/sdcard/DWARF_II")) {
                            // if (astro_mode_cmd_id == )
                        }
                    } else if (astro_mode_error_code == WS_SDCARD_WRITE_ERROR) {
                        SdCardInfo sdcard_info = system_.getSdCardAvailableAndTotal();
                        if (sdcard_info.available_size > 0) {
                            // if (astro_mode_cmd_id)
                        }
                    }
                }
            } else {
                log_i("astro_mode_error_code not found:%d", astro_mode_error_code);
                return 0;
            }
        } else {// 成功
            if (astro_mode_cmd_id == CMD_ASTRO_START_CAPTURE_RAW_DARK) {
                int has_sun_moon = focus_.checkWideSunMoon();
                if ((has_sun_moon & 0x0000001) == 0x0000001) {
                    if (focus_.getInfinityFocusPoint(infinity_focus_point_from_file)) {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_FAST_FOCUS_SUN_MOON).getITipsCode();
                    } else {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_SLOW_FOCUS_SUN_MOON).getITipsCode();
                    }
                } else {
                    if (focus_.getInfinityFocusPoint(infinity_focus_point_from_file)) {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_FAST_FOCUS_STAR).getITipsCode();
                    } else {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_SLOW_FOCUS_STAR).getITipsCode();
                    }
                }
            } else if (astro_mode_cmd_id == CMD_FOCUS_START_ASTRO_AUTO_FOCUS) {
                int has_sun_moon = focus_.checkWideSunMoon();
                if ((has_sun_moon & 0x0000001) == 0x0000001) {
                    itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_GOTO_SUN_MOON).getITipsCode();
                } else {
                    MEDIA_BUFFER dst_mb = cam_tele_.getRgbMB_4k(1920, 1080);
                    Mat img_src = Mat(1080, 1920, CV_8UC3, (unsigned char*)RK_MPI_MB_GetPtr(dst_mb));
                    if (StarDetection(img_src)) {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_NO_STAR).getITipsCode();
                    } else {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_CALIBRATION).getITipsCode();
                    }
                    rkfree(dst_mb);
                }
            } else if (astro_mode_cmd_id == CMD_ASTRO_START_CALIBRATION) {
                itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_GOTO_STAR).getITipsCode();
            } else if (astro_mode_cmd_id == CMD_ASTRO_START_GOTO_SOLAR_SYSTEM) {
                std::string target_name;
                astro_.gotoer_.getTargetName(target_name);

                if (target_name == "Sun" || target_name == "Moon") {
                    int has_sun_moon = focus_.checkWideSunMoon();
                    if ((has_sun_moon & 0x0000001) == 0x0000001) {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_LIVE_STACKING).getITipsCode();
                    } else {
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_NO_SUN_MOON).getITipsCode();
                    }
                } else {
                    itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_LIVE_STACKING).getITipsCode();
                }
            } else if (astro_mode_cmd_id == CMD_ASTRO_START_GOTO_DSO) {
                MEDIA_BUFFER dst_mb = cam_tele_.getRgbMB_4k(1920, 1080);
                Mat img_src = Mat(1080, 1920, CV_8UC3, (unsigned char*)RK_MPI_MB_GetPtr(dst_mb));
                if (StarDetection(img_src)) {
                    itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_NO_STAR).getITipsCode();
                } else {
                    ExposureSetting exp_setting;
                    cam_tele_.getExp(exp_setting);

                    IntValueSetting gain_setting;
                    cam_tele_.getGain(gain_setting);

                    if (exp_setting.value < 5.0 || gain_setting.value < 30 || gain_setting.value > 120)
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_ADJUST_SHOOT_PARAM).getITipsCode();
                    else
                        itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_LIVE_STACKING).getITipsCode();
                }
                rkfree(dst_mb);
            } else if (astro_mode_cmd_id == CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING) {
                itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_GO_LIVE).getITipsCode();
            } else if (astro_mode_cmd_id == CMD_ASTRO_GO_LIVE) {
                itips_code = ITipsCode(ASTRO_MODE, i_cmd_code, CODE_ITIPS_GUIDE_SUCCESS).getITipsCode();
            }
        }
    } else {
        int has_sun_moon = focus_.checkWideSunMoon();
        if ((has_sun_moon & 0x0000001) == 0x0000001) {
            if (focus_.getInfinityFocusPoint(infinity_focus_point_from_file)) {
                itips_code = ITipsCode(ASTRO_MODE, 0, CODE_ITIPS_GUIDE_FAST_FOCUS_SUN_MOON).getITipsCode();
            } else {
                itips_code = ITipsCode(ASTRO_MODE, 0, CODE_ITIPS_GUIDE_SLOW_FOCUS_SUN_MOON).getITipsCode();
            }
        } else {
            std::vector<DarkFrameParameter> dark_lost;
            astro_.checkLostDark(dark_lost);

            if (dark_lost.size() > 0) {
                itips_code = ITipsCode(ASTRO_MODE, 0, CODE_ITIPS_GUIDE_CAPTURE_DARK).getITipsCode();
            } else {
                if (focus_.getInfinityFocusPoint(infinity_focus_point_from_file)) {
                    itips_code = ITipsCode(ASTRO_MODE, 0, CODE_ITIPS_GUIDE_FAST_FOCUS_STAR).getITipsCode();
                } else {
                    itips_code = ITipsCode(ASTRO_MODE, 0, CODE_ITIPS_GUIDE_SLOW_FOCUS_STAR).getITipsCode();
                }
            }
        }
    }

    wsResITipsGet(CMD_ITIPS_GET, MODULE_ITIPS, CMD_TYPE_RESPONSE, WS_OK, ASTRO_MODE, itips_code);

    return 0;
}



int ITips::updateCmdIdAndErrorCode(int cmd_id, int error_code) {
    std::lock_guard<std::mutex> lock(error_code_mutex);

    if (cmd_id == CMD_ASTRO_START_CALIBRATION || cmd_id == CMD_ASTRO_START_GOTO_DSO || cmd_id == CMD_ASTRO_START_GOTO_SOLAR_SYSTEM ||
        cmd_id == CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING || cmd_id == CMD_ASTRO_START_CAPTURE_RAW_DARK || cmd_id == CMD_ASTRO_GO_LIVE || 
        cmd_id == CMD_FOCUS_START_ASTRO_AUTO_FOCUS) {

        old_astro_mode_cmd_id_ = astro_mode_cmd_id_;
        old_astro_mode_error_code_ = astro_mode_error_code_;
        astro_mode_cmd_id_ = cmd_id;
        astro_mode_error_code_ = error_code;
    } else {

    }

    return 0;
}

int ITips::backToOldCmdIdAndErrorCode(int cmd_id) {
    std::lock_guard<std::mutex> lock(error_code_mutex);

    if (cmd_id == CMD_ASTRO_START_CALIBRATION || cmd_id == CMD_ASTRO_START_GOTO_DSO || cmd_id == CMD_ASTRO_START_GOTO_SOLAR_SYSTEM ||
        cmd_id == CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING || cmd_id == CMD_ASTRO_START_CAPTURE_RAW_DARK || cmd_id == CMD_ASTRO_GO_LIVE || 
        cmd_id == CMD_FOCUS_START_ASTRO_AUTO_FOCUS) {

        astro_mode_cmd_id_ = old_astro_mode_cmd_id_;
        astro_mode_error_code_ = old_astro_mode_error_code_;
    } else {
        
    }

    return 0;
}

int ITips::getErrorCode(int cmd_id) {
    std::lock_guard<std::mutex> lock(error_code_mutex);

    if (cmd_id == CMD_ASTRO_START_CALIBRATION || cmd_id == CMD_ASTRO_START_GOTO_DSO || cmd_id == CMD_ASTRO_START_GOTO_SOLAR_SYSTEM ||
        cmd_id == CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING || cmd_id == CMD_ASTRO_START_CAPTURE_RAW_DARK || cmd_id == CMD_ASTRO_GO_LIVE || 
        cmd_id == CMD_FOCUS_START_ASTRO_AUTO_FOCUS) {

        return astro_mode_error_code_;
    } else {
        return 0;
    }
}

int ITips::getStarLastProcedureCmd(int cmd_id) {
    if (cmd_id == CMD_ASTRO_START_CAPTURE_RAW_DARK) 
        return 0;
    else if (cmd_id == CMD_FOCUS_START_ASTRO_AUTO_FOCUS) 
        return CMD_ASTRO_START_CAPTURE_RAW_DARK;
    else if (cmd_id == CMD_ASTRO_START_CALIBRATION)
        return CMD_FOCUS_START_ASTRO_AUTO_FOCUS;
    else if (cmd_id == CMD_ASTRO_START_GOTO_DSO)
        return CMD_ASTRO_START_CALIBRATION;
    else if (cmd_id == CMD_ASTRO_START_GOTO_SOLAR_SYSTEM)
        return CMD_ASTRO_START_CALIBRATION;
    else if (cmd_id == CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING)
        return CMD_ASTRO_START_GOTO_DSO;
    else if (cmd_id == CMD_ASTRO_GO_LIVE)
        return CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING;
    else
        return -1;
}

int ITips::getSunMoonLastProcedureCmd(int cmd_id) {
    if (cmd_id == CMD_FOCUS_START_ASTRO_AUTO_FOCUS) 
        return 0;
    else if (cmd_id == CMD_ASTRO_START_GOTO_SOLAR_SYSTEM) 
        return CMD_FOCUS_START_ASTRO_AUTO_FOCUS;
    else if (cmd_id == CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING)
        return CMD_ASTRO_START_GOTO_SOLAR_SYSTEM;
    else if (cmd_id == CMD_ASTRO_GO_LIVE)
        return CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING;
    else
        return -1;
}